﻿namespace MemoryTest_Communications.Model
{
    using System;
    using System.Collections.Generic;
    using System.Device.Location;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using WP7Contrib.Common;
    using WP7Contrib.Common.Helpers;
    using WP7Contrib.Communications;

    /// <summary>
    /// The property criteria.
    /// </summary>
    public sealed class PropertyCriteria : BaseModel, IEquatable<PropertyCriteria>, ICloneable<PropertyCriteria> 
    {
        #region Constants and Fields

        public const string GeoLocationRegularExpression = @"^(-?\d+\.\d+), (-?\d+\.\d+)$";
        public const string PostCodeRegularExpression = @"^([A-PR-UWYZ0-9][A-HK-Y0-9][AEHMNPRTVXY0-9]?[ABEHMNPRVWXY0-9]? {1,2}[0-9][ABD-HJLN-UW-Z]{2}|GIR 0AA)$";

        public const string GeoLocationFormat = "{0:N4}, {1:N4}";

        /// <summary>
        /// The centre point.
        /// </summary>
        private GeoCoordinate centrePoint;

        /// <summary>
        /// The keywords.
        /// </summary>
        private string keywords;

        /// <summary>
        /// The keywords exclude.
        /// </summary>
        private string keywordsExclude;

        /// <summary>
        /// The listing type.
        /// </summary>
        private ListingType listingType;

        /// <summary>
        /// The maximum bathrooms.
        /// </summary>
        private int? maximumBathrooms;

        /// <summary>
        /// The maximum bedrooms.
        /// </summary>
        private int? maximumBedrooms;

        /// <summary>
        /// The maximum price.
        /// </summary>
        private int? maximumPrice;

        /// <summary>
        /// The maximum rooms.
        /// </summary>
        private int? maximumRooms;

        /// <summary>
        /// The minimum bathrooms.
        /// </summary>
        private int? minimumBathrooms;

        /// <summary>
        /// The minimum bedrooms.
        /// </summary>
        private int? minimumBedrooms;

        /// <summary>
        /// The minimum price.
        /// </summary>
        private int? minimumPrice;

        /// <summary>
        /// The minimum rooms.
        /// </summary>
        private int? minimumRooms;

        /// <summary>
        /// The north east.
        /// </summary>
        private GeoCoordinate northEast;

        /// <summary>
        /// The place name.
        /// </summary>
        private string placeName;

        /// <summary>
        /// The property type.
        /// </summary>
        private PropertyType propertyType;

        /// <summary>
        /// The radius.
        /// </summary>
        private Radius radius;

        /// <summary>
        /// The south west.
        /// </summary>
        private GeoCoordinate southWest;

        /// <summary>
        /// The with images.
        /// </summary>
        private bool withImages;

        private SortOrder sortOrder;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyCriteria"/> class.
        /// </summary>
        public PropertyCriteria()
        {
            this.SetupSearchFields();
            this.SortOrderOptions = new List<SortOrder> {
                    SortOrder.BedroomNumberAscending, 
                    SortOrder.BedroomNumberDescending, 
                    SortOrder.Nestoria, 
                    SortOrder.Newest, 
                    SortOrder.Oldest, 
                    SortOrder.PriceAscending, 
                    SortOrder.PriceDescending
                };

            this.southWest = GeoCoordinate.Unknown;
            this.northEast = GeoCoordinate.Unknown;
            this.centrePoint = GeoCoordinate.Unknown;
            this.SortOrder = SortOrder.BedroomNumberAscending;
            this.radius = Radius.Unknown;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets CentrePoint.
        /// </summary>
        public GeoCoordinate CentrePoint
        {
            get
            {
                return this.centrePoint;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.centrePoint, value, () => this.CentrePoint);
            }
        }

        /// <summary>
        /// Gets or sets Keywords.
        /// </summary>
        public string Keywords
        {
            get
            {
                return this.keywords;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.keywords, StringHelper.Trim(value), () => this.Keywords);
            }
        }

        public SortOrder SortOrder
        {
            get
            {
                return this.sortOrder;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.sortOrder, value, () => this.SortOrder);
            }
        }

        /// <summary>
        /// Gets or sets KeywordsExclude.
        /// </summary>
        public string KeywordsExclude
        {
            get
            {
                return this.keywordsExclude;
            }

            set
            {
                this.SetPropertyAndNotify(
                    ref this.keywordsExclude, StringHelper.Trim(value), () => this.KeywordsExclude);
            }
        }

        /// <summary>
        /// Gets or sets ListingType.
        /// </summary>
        public ListingType ListingType
        {
            get
            {
                return this.listingType;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.listingType, value, () => this.ListingType);
            }
        }

        /// <summary>
        /// Gets MaxBathroomRange.
        /// </summary>
        public List<int> MaxBathroomRange { get; private set; }

        /// <summary>
        /// Gets MaxBedroomRange.
        /// </summary>
        public List<int> MaxBedroomRange { get; private set; }

        /// <summary>
        /// Gets or sets MaxBuyPriceRange.
        /// </summary>
        public List<int> MaxBuyPriceRange { get; set; }

        /// <summary>
        /// Gets or sets MaxRentalPriceRange.
        /// </summary>
        public List<int> MaxRentalPriceRange { get; set; }

        /// <summary>
        /// Gets or sets MaximumBathrooms.
        /// </summary>
        public int? MaximumBathrooms
        {
            get
            {
                return this.maximumBathrooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.maximumBathrooms, value, () => this.MaximumBathrooms);
            }
        }

        /// <summary>
        /// Gets or sets MaximumBedrooms.
        /// </summary>
        public int? MaximumBedrooms
        {
            get
            {
                return this.maximumBedrooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.maximumBedrooms, value, () => this.MaximumBedrooms);
            }
        }

        /// <summary>
        /// Gets or sets MaximumPrice.
        /// </summary>
        public int? MaximumPrice
        {
            get
            {
                return this.maximumPrice;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.maximumPrice, value, () => this.MaximumPrice);
            }
        }

        /// <summary>
        /// Gets or sets MaximumRooms.
        /// </summary>
        public int? MaximumRooms
        {
            get
            {
                return this.maximumRooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.maximumRooms, value, () => this.MaximumRooms);
            }
        }

        /// <summary>
        /// Gets MinBathroomRange.
        /// </summary>
        public List<int> MinBathroomRange { get; private set; }

        /// <summary>
        /// Gets MinBedroomRange.
        /// </summary>
        public List<int> MinBedroomRange { get; private set; }

        /// <summary>
        /// Gets or sets MinBuyPriceRange.
        /// </summary>
        public List<int> MinBuyPriceRange { get; private set; }

        /// <summary>
        /// Gets or sets MinRentalPriceRange.
        /// </summary>
        public List<int> MinRentalPriceRange { get; private set; }

        /// <summary>
        /// Gets or sets MinimumBathrooms.
        /// </summary>
        public int? MinimumBathrooms
        {
            get
            {
                return this.minimumBathrooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.minimumBathrooms, value, () => this.MinimumBathrooms);
            }
        }

        /// <summary>
        /// Gets or sets MinimumBedrooms.
        /// </summary>
        public int? MinimumBedrooms
        {
            get
            {
                return this.minimumBedrooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.minimumBedrooms, value, () => this.MinimumBedrooms);
            }
        }

        /// <summary>
        /// Gets or sets MinimumPrice.
        /// </summary>
        public int? MinimumPrice
        {
            get
            {
                return this.minimumPrice;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.minimumPrice, value, () => this.MinimumPrice);
            }
        }

        /// <summary>
        /// Gets or sets MinimumRooms.
        /// </summary>
        public int? MinimumRooms
        {
            get
            {
                return this.minimumRooms;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.minimumRooms, value, () => this.MinimumRooms);
            }
        }

        /// <summary>
        /// Gets or sets NorthEast.
        /// </summary>
        public GeoCoordinate NorthEast
        {
            get
            {
                return this.northEast;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.northEast, value, () => this.NorthEast);
            }
        }

        /// <summary>
        /// Gets or sets PlaceName.
        /// </summary>
        public string PlaceName
        {
            get
            {
                return this.placeName;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.placeName, StringHelper.Trim(value), () => this.PlaceName);
            }
        }

        /// <summary>
        /// Gets or sets PropertyType.
        /// </summary>
        public PropertyType PropertyType
        {
            get
            {
                return this.propertyType;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.propertyType, value, () => this.PropertyType);
            }
        }

        /// <summary>
        /// Gets or sets Radius.
        /// </summary>
        public Radius Radius
        {
            get
            {
                return this.radius;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.radius, value, () => this.Radius);
            }
        }

        /// <summary>
        /// Gets or sets SortOrderOptions.
        /// </summary>
        public List<SortOrder> SortOrderOptions { get; set; }

        /// <summary>
        /// Gets or sets SouthWest.
        /// </summary>
        public GeoCoordinate SouthWest
        {
            get
            {
                return this.southWest;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.southWest, value, () => this.SouthWest);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether WithImages.
        /// </summary>
        public bool WithImages
        {
            get
            {
                return this.withImages;
            }

            set
            {
                this.SetPropertyAndNotify(ref this.withImages, value, () => this.WithImages);
            }
        }

        public bool HasCentrePoint
        {
            get { return CentrePoint != GeoCoordinate.Unknown; }
        }

        #endregion

        #region Operators

        /// <summary>
        /// The ==.
        /// </summary>
        /// <param name="c1">
        /// The c 1.
        /// </param>
        /// <param name="c2">
        /// The c 2.
        /// </param>
        /// <returns>
        /// </returns>
        public static bool operator ==(PropertyCriteria c1, PropertyCriteria c2)
        {
            return Equals(c1, c2);
        }

        /// <summary>
        /// The !=.
        /// </summary>
        /// <param name="c1">
        /// The c 1.
        /// </param>
        /// <param name="c2">
        /// The c 2.
        /// </param>
        /// <returns>
        /// </returns>
        public static bool operator !=(PropertyCriteria c1, PropertyCriteria c2)
        {
            return !Equals(c1, c2);
        }

        #endregion

        #region Public Methods

        public static bool IsGeoLocation(string address)
        {
            if (string.IsNullOrEmpty(address))
                return false;

            var latLongExp = new Regex( GeoLocationRegularExpression);
            return latLongExp.IsMatch(address.Trim());
        }

        public static bool IsPostCode(string address)
        {
            if (string.IsNullOrEmpty(address))
                return false;

            var latLongExp = new Regex(PostCodeRegularExpression);
            return latLongExp.IsMatch(address.Trim());
        }

        /// <summary>
        /// The equals.
        /// </summary>
        /// <param name="obj">
        /// The obj.
        /// </param>
        /// <returns>
        /// The equals.
        /// </returns>
        public override bool Equals(object obj)
        {
            if (ReferenceEquals(null, obj))
            {
                return false;
            }

            return obj is PropertyCriteria && this.Equals((PropertyCriteria)obj);
        }

        /// <summary>
        /// The get hash code.
        /// </summary>
        /// <returns>
        /// The get hash code.
        /// </returns>
        public override int GetHashCode()
        {
            return
                this.CombineHashCodes(
                    new object[]
                        {
                            this.Keywords, this.KeywordsExclude, this.ListingType,
                            this.MaximumBathrooms, this.MaximumBedrooms, this.MaximumPrice, this.MaximumRooms,
                            this.CombineHashCodes(this.MinRentalPriceRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MaxRentalPriceRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MinBathroomRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MaxBathroomRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MinBedroomRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MaxBedroomRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MinBuyPriceRange.Cast<object>().ToArray()),
                            this.CombineHashCodes(this.MaxBuyPriceRange.Cast<object>().ToArray()),
                            this.MinimumBathrooms, this.MinimumBedrooms, this.MinimumPrice, this.MinimumRooms, 
                            this.PropertyType, 
                            this.WithImages, this.MinimumBedrooms, this.MinimumBedrooms,
                            this.Radius,
                            this.PlaceName, 
                            this.NorthEast,
                            this.SouthWest,
                            this.CentrePoint,
                            this.SortOrder
                        });
        }

        /// <summary>
        /// The serialize to string.
        /// </summary>
        /// <param name="propertyEncoder">
        /// The property Encoder.
        /// </param>
        /// <returns>
        /// The serialize to string.
        /// </returns>
        public string SerializeToString(IEncodeProperties propertyEncoder)
        {
            var sb = new StringBuilder();

            sb.AppendFormat("&withimage_only={0}", this.WithImages ? "1" : "0");
            sb.AppendFormat("&keywords{0}=", propertyEncoder.Encode(this.Keywords));
            sb.AppendFormat("&keywords_exclude={0}", propertyEncoder.Encode(this.KeywordsExclude));

            sb.AppendFormat("&place_name={0}", propertyEncoder.Encode(this.PlaceName));

            if (this.SouthWest != GeoCoordinate.Unknown)
            {
                sb.AppendFormat("&southwest={0},{1}", this.SouthWest.Latitude, this.SouthWest.Longitude);
            }

            if (this.NorthEast != GeoCoordinate.Unknown)
            {
                sb.AppendFormat("&northeast={0},{1}", this.NorthEast.Latitude, this.NorthEast.Longitude);
            }

            if (this.CentrePoint != GeoCoordinate.Unknown)
            {
                sb.AppendFormat("&centre_point={0},{1}", this.CentrePoint.Latitude, this.CentrePoint.Longitude);
            }

            if (this.Radius != Radius.Unknown)
            {
                sb.AppendFormat(
                    "&radius={0},{1},{2}{3}", 
                    this.CentrePoint.Latitude, 
                    this.CentrePoint.Longitude, 
                    this.Radius.Distance, 
                    this.Radius.DistanceUnit);
            }

            sb.AppendFormat("&property_type={0}", propertyEncoder.Encode(this.PropertyTypeAsString()));
            sb.AppendFormat("&listing_type={0}", propertyEncoder.Encode(this.ListingTypeAsString()));

            sb.AppendFormat(
                "&bedroom_min={0}", this.MinimumBedrooms.HasValue ? this.MinimumBedrooms.Value.ToString() : "min");
            sb.AppendFormat(
                "&bedroom_max={0}", this.MaximumBedrooms.HasValue ? this.MaximumBedrooms.Value.ToString() : "max");
            sb.AppendFormat(
                "&bathroom_min={0}", this.MinimumBathrooms.HasValue ? this.MinimumBathrooms.Value.ToString() : "min");
            sb.AppendFormat(
                "&bathroom_max={0}", this.MaximumBathrooms.HasValue ? this.MaximumBathrooms.Value.ToString() : "max");
            sb.AppendFormat("&room_min={0}", this.MinimumRooms.HasValue ? this.MinimumRooms.Value.ToString() : "min");
            sb.AppendFormat("&room_max={0}", this.MaximumRooms.HasValue ? this.MaximumRooms.Value.ToString() : "max");
            sb.AppendFormat("&price_min={0}", this.MinimumPrice.HasValue ? this.MinimumPrice.Value.ToString() : "min");
            sb.AppendFormat("&price_max={0}", this.MaximumPrice.HasValue ? this.MaximumPrice.Value.ToString() : "max");
            sb.AppendFormat("&sort={0}", propertyEncoder.Encode(this.SortOrderTypeAsString()));

            return sb.ToString();
        }

        #endregion

        #region Implemented Interfaces

        #region IEquatable<PropertyCriteria>

        /// <summary>
        /// The equals.
        /// </summary>
        /// <param name="criteria">
        /// The criteria.
        /// </param>
        /// <returns>
        /// The equals.
        /// </returns>
        public bool Equals(PropertyCriteria criteria)
        {
            if (ReferenceEquals(null, criteria))
            {
                return false;
            }

            if (this.Keywords != criteria.Keywords)
            {
                return false;
            }

            if (this.KeywordsExclude != criteria.KeywordsExclude)
            {
                return false;
            }

            if (this.ListingType != criteria.ListingType)
            {
                return false;
            }

            if (!this.MaxBathroomRange.SequenceEqual(criteria.MaxBathroomRange))
            {
                return false;
            }

            if (!this.MaxBedroomRange.SequenceEqual(criteria.MaxBedroomRange))
            {
                return false;
            }

            if (!this.MaxBuyPriceRange.SequenceEqual(criteria.MaxBuyPriceRange))
            {
                return false;
            }

            if (this.MaximumBathrooms != criteria.MaximumBathrooms)
            {
                return false;
            }

            if (this.MaximumBedrooms != criteria.MaximumBedrooms)
            {
                return false;
            }

            if (this.MaximumPrice != criteria.MaximumPrice)
            {
                return false;
            }

            if (this.MaximumRooms != criteria.MaximumRooms)
            {
                return false;
            }

            if (!this.MaxRentalPriceRange.SequenceEqual(criteria.MaxRentalPriceRange))
            {
                return false;
            }

            if (!this.MinBathroomRange.SequenceEqual(criteria.MinBathroomRange))
            {
                return false;
            }

            if (!this.MinBedroomRange.SequenceEqual(criteria.MinBedroomRange))
            {
                return false;
            }

            if (!this.MinBuyPriceRange.SequenceEqual(criteria.MinBuyPriceRange))
            {
                return false;
            }

            if (this.MinimumBathrooms != criteria.MinimumBathrooms)
            {
                return false;
            }

            if (this.MinimumBedrooms != criteria.MinimumBedrooms)
            {
                return false;
            }

            if (this.MinimumPrice != criteria.MinimumPrice)
            {
                return false;
            }

            if (this.MinimumRooms != criteria.MinimumRooms)
            {
                return false;
            }

            if (!this.MinRentalPriceRange.SequenceEqual(criteria.MinRentalPriceRange))
            {
                return false;
            }

            if (this.PropertyType != criteria.PropertyType)
            {
                return false;
            }

            if (this.WithImages != criteria.WithImages)
            {
                return false;
            }

            if (this.MinimumBedrooms != criteria.MinimumBedrooms)
            {
                return false;
            }

            if (this.MinimumBedrooms != criteria.MinimumBedrooms)
            {
                return false;
            }

            if (this.Radius != criteria.Radius)
            {
                return false;
            }

            if (this.PlaceName != criteria.PlaceName)
            {
                return false;
            }

            if (this.NorthEast != criteria.NorthEast)
            {
                return false;
            }

            if (this.SouthWest != criteria.SouthWest)
            {
                return false;
            }

            if (this.CentrePoint != criteria.CentrePoint)
            {
                return false;
            }

            if (this.Radius != criteria.Radius)
            {
                return false;
            }

            return true;
        }

        #endregion

        #region ICloneable<PropertyCriteria>

        public PropertyCriteria ShallowClone()
        {
            return CloneImpl();
        }

        public PropertyCriteria DeepClone()
        {
            return CloneImpl();
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// The listing type as string.
        /// </summary>
        /// <returns>
        /// The listing type as string.
        /// </returns>
        private string ListingTypeAsString()
        {
            return this.ListingType == ListingType.Undefined ? null : this.ListingType.ToString().ToLowerInvariant();
        }

        /// <summary>
        /// The property type as string.
        /// </summary>
        /// <returns>
        /// The property type as string.
        /// </returns>
        private string PropertyTypeAsString()
        {
            return this.PropertyType == PropertyType.Undefined ? null : this.PropertyType.ToString().ToLowerInvariant();
        }

        private string SortOrderTypeAsString()
        {
            return this.SortOrder.ToString().ToLowerInvariant();
        }

        /// <summary>
        /// The setup search fields.
        /// </summary>
        private void SetupSearchFields()
        {
            this.MinBathroomRange = new List<int> { 1, 2, 3, 4, 5 };

            this.MaxBathroomRange = new List<int> { 1, 2, 3, 4, 5 };

            this.MinBedroomRange = new List<int> { 1, 2, 3, 4, 5 };

            this.MaxBedroomRange = new List<int> { 1, 2, 3, 4, 5 };

            this.MinRentalPriceRange = new List<int> {
                    500, 
                    550, 
                    600, 
                    650, 
                    700, 
                    750, 
                    800, 
                    850, 
                    900, 
                    950, 
                    1000, 
                    1100, 
                    1200, 
                    1300, 
                    1400, 
                    1500, 
                    1600, 
                    1800, 
                    1900, 
                    2000, 
                    2200, 
                    2400, 
                    2600, 
                    2800, 
                    3000
                };

            this.MaxRentalPriceRange = new List<int> {
                    3000, 
                    2800, 
                    2600, 
                    2400, 
                    2200, 
                    2000, 
                    1900, 
                    1800, 
                    1700, 
                    1600, 
                    1500, 
                    1400, 
                    1300, 
                    1200, 
                    1100, 
                    1000, 
                    950, 
                    900, 
                    850, 
                    800, 
                    750, 
                    700, 
                    650, 
                    600, 
                    550
                };

            this.MinBuyPriceRange = new List<int> {
                    50, 
                    55, 
                    60, 
                    65, 
                    70, 
                    75, 
                    80, 
                    85, 
                    90, 
                    100, 
                    150, 
                    200, 
                    250, 
                    300, 
                    350, 
                    400, 
                    450, 
                    500, 
                    550, 
                    600, 
                    650, 
                    700, 
                    750, 
                    800, 
                    850, 
                    900, 
                    950, 
                    1000
                };

            this.MaxBuyPriceRange = new List<int> {
                    1000, 
                    950, 
                    900, 
                    850, 
                    800, 
                    750, 
                    700, 
                    650, 
                    600, 
                    550, 
                    500, 
                    450, 
                    400, 
                    350, 
                    300, 
                    250, 
                    200, 
                    150, 
                    100, 
                    95, 
                    90, 
                    85, 
                    80, 
                    75, 
                    70, 
                    65, 
                    60, 
                    55
                };
        }

        private PropertyCriteria CloneImpl()
        {
            var clone = new PropertyCriteria
                            {
                                CentrePoint = new GeoCoordinate(this.CentrePoint.Latitude, this.CentrePoint.Longitude),
                                Keywords = this.Keywords,
                                keywordsExclude = this.keywordsExclude,
                                ListingType = this.ListingType,
                                MaxBathroomRange = this.MaxBathroomRange,
                                MaxBedroomRange = this.MaxBedroomRange,
                                MaxBuyPriceRange = this.MaxBuyPriceRange,
                                MaximumBathrooms = this.MaximumBathrooms,
                                MaximumBedrooms = this.MaximumBedrooms,
                                MaximumPrice = this.MaximumPrice,
                                MaximumRooms = this.MaximumRooms,
                                MaxRentalPriceRange = this.MaxRentalPriceRange,
                                MinBathroomRange = this.MinBathroomRange,
                                MinBedroomRange = this.MinBedroomRange,
                                MinBuyPriceRange = this.MinBuyPriceRange,
                                MinimumBathrooms = this.MinimumBathrooms,
                                MinimumBedrooms = this.MinimumBedrooms,
                                MinimumPrice = this.MinimumPrice,
                                MinimumRooms = this.MinimumRooms,
                                MinRentalPriceRange = this.MinRentalPriceRange,
                                NorthEast = new GeoCoordinate(this.NorthEast.Latitude, this.NorthEast.Longitude),
                                PlaceName = this.PlaceName,
                                PropertyType = this.PropertyType,
                                Radius =
                                    new Radius(new GeoCoordinate(this.Radius.Latitude, this.Radius.Longitude),
                                               this.Radius.Distance, this.Radius.DistanceUnit),
                                SortOrder = this.SortOrder,
                                SortOrderOptions = this.SortOrderOptions,
                                SouthWest = new GeoCoordinate(this.SouthWest.Latitude, this.SouthWest.Longitude),
                                WithImages = this.WithImages
                            };

            return clone;
        }

        #endregion
    }
}